1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 import java.math.*;
32 import java.util.*;
33
34 public class ZeroScalingTests {
35
36 static MathContext longEnough = new MathContext(50, RoundingMode.UNNECESSARY);
37
38 static BigDecimal[] zeros = new BigDecimal[23];
39 static {
40 for(int i = 0; i < 21; i++) {
41 zeros[i] = new BigDecimal(BigInteger.ZERO, i-10);
42 }
43 zeros[21] = new BigDecimal(BigInteger.ZERO, Integer.MIN_VALUE);
44 zeros[22] = new BigDecimal(BigInteger.ZERO, Integer.MAX_VALUE);
45 }
46
47 static BigDecimal element = BigDecimal.valueOf(100, -2);
48
49 static MathContext contexts[] = {
50 new MathContext(0, RoundingMode.UNNECESSARY),
51 new MathContext(100, RoundingMode.UNNECESSARY),
52 new MathContext(5, RoundingMode.UNNECESSARY),
53 new MathContext(4, RoundingMode.UNNECESSARY),
54 new MathContext(3, RoundingMode.UNNECESSARY),
55 new MathContext(2, RoundingMode.UNNECESSARY),
56 new MathContext(1, RoundingMode.UNNECESSARY),
57 };
58
59
60 static int addTests() {
61 int failures = 0;
62
63 for(BigDecimal zero1: zeros) {
64 for(BigDecimal zero2: zeros) {
65 BigDecimal expected = new BigDecimal(BigInteger.ZERO,
66 Math.max(zero1.scale(), zero2.scale()));
67 BigDecimal result;
68
69 if(! (result=zero1.add(zero2)).equals(expected) ) {
70 failures++;
71 System.err.println("For classic exact add, expected scale of " +
72 expected.scale() + "; got " +
73 result.scale() + ".");
74 }
75
76 if(! (result=zero1.add(zero2, MathContext.UNLIMITED)).equals(expected) ) {
77 failures++;
78 System.err.println("For UNLIMITED math context add," +
79 " expected scale of " +
80 expected.scale() + "; got " +
81 result.scale() + ".");
82 }
83
84 if(! (result=zero1.add(zero2, longEnough)).equals(expected) ) {
85 failures++;
86 System.err.println("For longEnough math context add," +
87 " expected scale of " +
88 expected.scale() + "; got " +
89 result.scale() + ".");
90 }
91
92 }
93 }
94
95
96 for (MathContext mc: contexts) {
97 for (BigDecimal zero: zeros) {
98 if (Math.abs((long)zero.scale()) < 100 ) {
99
100 int preferredScale = Math.max(zero.scale(), element.scale());
101 if (mc.getPrecision() != 0) {
102 if (preferredScale < -4 )
103 preferredScale = -4;
104 else if (preferredScale > -(5 - mc.getPrecision())) {
105 preferredScale = -(5 - mc.getPrecision());
106 }
107 }
108
109
110
111
112
113
114
115
116
117
118 BigDecimal result = element.add(zero, mc);
119 if (result.scale() != preferredScale ||
120 result.compareTo(element) != 0) {
121 failures++;
122 System.err.println("Expected scale " + preferredScale +
123 " result scale was " + result.scale() +
124 " ; value was " + result);
125 }
126
127 result = zero.add(element, mc);
128 if (result.scale() != preferredScale ||
129 result.compareTo(element) != 0) {
130 failures++;
131 System.err.println("Expected scale " + preferredScale +
132 " result scale was " + result.scale() +
133 " ; value was " + result);
134 }
135
136 result = element.negate().add(zero, mc);
137 if (result.scale() != preferredScale ||
138 result.compareTo(element.negate()) != 0) {
139 failures++;
140 System.err.println("Expected scale " + preferredScale +
141 " result scale was " + result.scale() +
142 " ; value was " + result);
143 }
144
145 result = zero.add(element.negate(), mc);
146 if (result.scale() != preferredScale ||
147 result.compareTo(element.negate()) != 0) {
148 failures++;
149 System.err.println("Expected scale " + preferredScale +
150 " result scale was " + result.scale() +
151 " ; value was " + result);
152 }
153
154 }
155 }
156 }
157
158 return failures;
159 }
160
161 static int subtractTests() {
162 int failures = 0;
163
164 for(BigDecimal zero1: zeros) {
165 for(BigDecimal zero2: zeros) {
166 BigDecimal expected = new BigDecimal(BigInteger.ZERO,
167 Math.max(zero1.scale(), zero2.scale()));
168 BigDecimal result;
169
170 if(! (result=zero1.subtract(zero2)).equals(expected) ) {
171 failures++;
172 System.err.println("For classic exact subtract, expected scale of " +
173 expected.scale() + "; got " +
174 result.scale() + ".");
175 }
176
177 if(! (result=zero1.subtract(zero2, MathContext.UNLIMITED)).equals(expected) ) {
178 failures++;
179 System.err.println("For UNLIMITED math context subtract," +
180 " expected scale of " +
181 expected.scale() + "; got " +
182 result.scale() + ".");
183 }
184
185 if(! (result=zero1.subtract(zero2, longEnough)).equals(expected) ) {
186 failures++;
187 System.err.println("For longEnough math context subtract," +
188 " expected scale of " +
189 expected.scale() + "; got " +
190 result.scale() + ".");
191 }
192
193 }
194 }
195
196
197
198 for (MathContext mc: contexts) {
199 for (BigDecimal zero: zeros) {
200 if (Math.abs((long)zero.scale()) < 100 ) {
201
202 int preferredScale = Math.max(zero.scale(), element.scale());
203 if (mc.getPrecision() != 0) {
204 if (preferredScale < -4 )
205 preferredScale = -4;
206 else if (preferredScale > -(5 - mc.getPrecision())) {
207 preferredScale = -(5 - mc.getPrecision());
208 }
209 }
210
211
212
213
214
215
216
217
218
219
220 BigDecimal result = element.subtract(zero, mc);
221 if (result.scale() != preferredScale ||
222 result.compareTo(element) != 0) {
223 failures++;
224 System.err.println("Expected scale " + preferredScale +
225 " result scale was " + result.scale() +
226 " ; value was " + result);
227 }
228
229 result = zero.subtract(element, mc);
230 if (result.scale() != preferredScale ||
231 result.compareTo(element.negate()) != 0) {
232 failures++;
233 System.err.println("Expected scale " + preferredScale +
234 " result scale was " + result.scale() +
235 " ; value was " + result);
236 }
237
238 result = element.negate().subtract(zero, mc);
239 if (result.scale() != preferredScale ||
240 result.compareTo(element.negate()) != 0) {
241 failures++;
242 System.err.println("Expected scale " + preferredScale +
243 " result scale was " + result.scale() +
244 " ; value was " + result);
245 }
246
247 result = zero.subtract(element.negate(), mc);
248 if (result.scale() != preferredScale ||
249 result.compareTo(element) != 0) {
250 failures++;
251 System.err.println("Expected scale " + preferredScale +
252 " result scale was " + result.scale() +
253 " ; value was " + result);
254 }
255
256 }
257 }
258 }
259
260 return failures;
261 }
262
263 static int multiplyTests() {
264 int failures = 0;
265
266 BigDecimal ones[] = {
267 BigDecimal.valueOf(1, 0),
268 BigDecimal.valueOf(10, 1),
269 BigDecimal.valueOf(1000, 3),
270 BigDecimal.valueOf(100000000, 8),
271 };
272
273 List<BigDecimal> values = new LinkedList<BigDecimal>();
274 values.addAll(Arrays.asList(zeros));
275 values.addAll(Arrays.asList(ones));
276
277 for(BigDecimal zero1: zeros) {
278 for(BigDecimal value: values) {
279 BigDecimal expected = new BigDecimal(BigInteger.ZERO,
280 (int)Math.min(Math.max((long)zero1.scale()+value.scale(),
281 Integer.MIN_VALUE ),
282 Integer.MAX_VALUE ) );
283 BigDecimal result;
284
285 if(! (result=zero1.multiply(value)).equals(expected) ) {
286 failures++;
287 System.err.println("For classic exact multiply, expected scale of " +
288 expected.scale() + "; got " +
289 result.scale() + ".");
290 }
291
292 if(! (result=zero1.multiply(value, MathContext.UNLIMITED)).equals(expected) ) {
293 failures++;
294 System.err.println("For UNLIMITED math context multiply," +
295 " expected scale of " +
296 expected.scale() + "; got " +
297 result.scale() + ".");
298 }
299
300 if(! (result=zero1.multiply(value, longEnough)).equals(expected) ) {
301 failures++;
302 System.err.println("For longEnough math context multiply," +
303 " expected scale of " +
304 expected.scale() + "; got " +
305 result.scale() + ".");
306 }
307
308 }
309 }
310
311 return failures;
312 }
313
314 static int divideTests() {
315 int failures = 0;
316
317 BigDecimal [] ones = {
318 BigDecimal.valueOf(1, 0),
319 BigDecimal.valueOf(10, -1),
320 BigDecimal.valueOf(100, -2),
321 BigDecimal.valueOf(1000, -3),
322 BigDecimal.valueOf(1000000, -5),
323 };
324
325 for(BigDecimal one: ones) {
326 for(BigDecimal zero: zeros) {
327 BigDecimal expected = new BigDecimal(BigInteger.ZERO,
328 (int)Math.min(Math.max((long)zero.scale() - one.scale(),
329 Integer.MIN_VALUE ),
330 Integer.MAX_VALUE ) );
331 BigDecimal result;
332
333 if(! (result=zero.divide(one)).equals(expected) ) {
334 failures++;
335 System.err.println("For classic exact divide, expected scale of " +
336 expected.scale() + "; got " +
337 result.scale() + ".");
338 }
339
340 if(! (result=zero.divide(one, MathContext.UNLIMITED)).equals(expected) ) {
341 failures++;
342 System.err.println("For UNLIMITED math context divide," +
343 " expected scale of " +
344 expected.scale() + "; got " +
345 result.scale() + ".");
346 }
347
348 if(! (result=zero.divide(one, longEnough)).equals(expected) ) {
349 failures++;
350 System.err.println("For longEnough math context divide," +
351 " expected scale of " +
352 expected.scale() + "; got " +
353 result.scale() + ".");
354 }
355
356 }
357 }
358
359 return failures;
360 }
361
362 static int setScaleTests() {
363 int failures = 0;
364
365 int scales[] = {
366 Integer.MIN_VALUE,
367 Integer.MIN_VALUE+1,
368 -10000000,
369 -3,
370 -2,
371 -1,
372 0,
373 1,
374 2,
375 3,
376 10,
377 10000000,
378 Integer.MAX_VALUE-1,
379 Integer.MAX_VALUE
380 };
381
382 for(BigDecimal zero: zeros) {
383 for(int scale: scales) {
384 try {
385 BigDecimal bd = zero.setScale(scale);
386 }
387 catch (ArithmeticException e) {
388 failures++;
389 System.err.println("Exception when trying to set a scale of " + scale +
390 " on " + zero);
391 }
392 }
393 }
394
395 return failures;
396 }
397
398 static int toEngineeringStringTests() {
399 int failures = 0;
400
401 String [][] testCases = {
402 {"0E+10", "0.00E+12"},
403 {"0E+9", "0E+9"},
404 {"0E+8", "0.0E+9"},
405 {"0E+7", "0.00E+9"},
406
407 {"0E-10", "0.0E-9"},
408 {"0E-9", "0E-9"},
409 {"0E-8", "0.00E-6"},
410 {"0E-7", "0.0E-6"},
411 };
412
413 for(String[] testCase: testCases) {
414 BigDecimal bd = new BigDecimal(testCase[0]);
415 String result = bd.toEngineeringString();
416
417 if (!result.equals(testCase[1]) ||
418 !bd.equals(new BigDecimal(result))) {
419 failures++;
420 System.err.println("From input ``" + testCase[0] + ",'' " +
421 " bad engineering string output ``" + result +
422 "''; expected ``" + testCase[1] + ".''");
423 }
424
425 }
426
427 return failures;
428 }
429
430 static int ulpTests() {
431 int failures = 0;
432
433 for(BigDecimal zero: zeros) {
434 BigDecimal result;
435 BigDecimal expected = BigDecimal.valueOf(1, zero.scale());
436
437 if (! (result=zero.ulp()).equals(expected) ) {
438 failures++;
439 System.err.println("Unexpected ulp value for zero value " +
440 zero + "; expected " + expected +
441 ", got " + result);
442 }
443 }
444
445 return failures;
446 }
447
448 public static void main(String argv[]) {
449 int failures = 0;
450
451 failures += addTests();
452 failures += subtractTests();
453 failures += multiplyTests();
454 failures += divideTests();
455 failures += setScaleTests();
456 failures += toEngineeringStringTests();
457 failures += ulpTests();
458
459 if (failures > 0 ) {
460 throw new RuntimeException("Incurred " + failures + " failures" +
461 " testing the preservation of zero scales.");
462 }
463 }
464 }